
#include "PreviewWindow.h"
#include "MainFrame.h"
#include "FontsetPanelExt.h"
#include "wx/dcclient.h"
#include "wx/dcmemory.h"
#include "wx/image.h"
#include "wx/log.h"

#include "ft2build.h"
#include FT_FREETYPE_H
#include FT_BITMAP_H
#include FT_GLYPH_H

BEGIN_EVENT_TABLE(PreviewWindow, wxScrolledWindow)
    EVT_PAINT  (PreviewWindow::OnPaint)
    //EVT_ERASE_BACKGROUND (PreviewWindow::OnEraseBackground)
END_EVENT_TABLE()

PreviewWindow::PreviewWindow( wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxString& name )
    : wxScrolledWindow( parent, id, pos, size, style, name ),
      mDisplayGrid( true ),
      mDisplayWidths( true )
{
}

PreviewWindow::~PreviewWindow(void)
{
}

//UpdateDisplay
void PreviewWindow::UpdateDisplay( const FontDefinition& fontDef )
{
    // Set the canvas size:
    SetVirtualSize( fontDef.mImageWidth, fontDef.mImageHeight );

    // Generate the preview image
    wxMemoryDC canvasDC, fontDC, gridDC, widthDC;
    mCanvas.Create( fontDef.mImageWidth, fontDef.mImageHeight, 24 );
    canvasDC.SelectObject( mCanvas );
    canvasDC.SetBackground( *wxBLACK_BRUSH );
    canvasDC.Clear();
    canvasDC.SelectObject( wxNullBitmap );

    //mPreviewImage.Create( fontDef.mImageWidth, fontDef.mImageHeight, 32 );
    unsigned char* alphaBuf = (unsigned char*)malloc( fontDef.mImageWidth * fontDef.mImageHeight );
    memset( alphaBuf, 0, fontDef.mImageWidth * fontDef.mImageHeight );
    mPreviewImage.Create( fontDef.mImageWidth, fontDef.mImageHeight, 32 );
    mPreviewImage.SetAlpha( alphaBuf );
    mGridOverlay.Create( fontDef.mImageWidth, fontDef.mImageHeight, 24 );
    mWidthsOverlay.Create( fontDef.mImageWidth, fontDef.mImageHeight, 24 );

    gridDC.SelectObject( mGridOverlay );
    gridDC.SetBackground( *wxTRANSPARENT_BRUSH );
    gridDC.SetPen( *wxWHITE_PEN );
    gridDC.Clear();

    widthDC.SelectObject( mWidthsOverlay );
    widthDC.SetBackground( *wxTRANSPARENT_BRUSH );
    widthDC.SetPen( *wxCYAN_PEN );
    widthDC.Clear();

    // Draw the glyphs:
    int hCells = fontDef.mImageWidth / fontDef.mCellWidth;
    int vCells = fontDef.mImageHeight / fontDef.mCellHeight;
    FT_Library fontLib;
    FT_Error error = FT_Init_FreeType( &fontLib );
    FT_Face face;
    error = FT_New_Face( fontLib, fontDef.mFontFileName.c_str(), 0, &face );
    if ( !error && face )
    {
        error = FT_Set_Pixel_Sizes( face, fontDef.mFontWidth, fontDef.mFontHeight );
        //error = FT_Set_Char_Size( face, fontDef.mFontWidth * 64, fontDef.mFontHeight * 64, 0, 0 );
        bool isScalable = FT_IS_SCALABLE( face );
        bool antiAliased = ( fontDef.mAntiAliasMode == FontDefinition::Normal );

        // Generate the glyphs into an array.  We need to calculate the baseline
        // by going over each character and examining the value of its bitmap_top.
        // The maximum bitmap_top will be used to calculate the baseline.
        int maxTop = 0, maxLeft = 0;
        int curGlyph = fontDef.mStartCharacter;
        std::vector< FT_Glyph > glyphArray;
        glyphArray.resize( hCells * vCells );
        mGlyphWidths.resize( hCells * vCells );
        for ( int y = 0; y < vCells; y++ )
        {
            int top = y * fontDef.mCellHeight;
            int bottom = top + fontDef.mCellHeight;
            for ( int x = 0; x < hCells; x++ )
            {
                int left = x * fontDef.mCellWidth;
                int curIndex = y * hCells + x;

                // Convert the ASCII character index to the glyph index
                int glyphIndex = FT_Get_Char_Index( face, curGlyph );

                // Load the glyph
                error = FT_Load_Glyph( face, glyphIndex, FT_LOAD_DEFAULT );
                if ( error )
                    continue;

                // Get the glyph and store it in the array:
                FT_GlyphSlot slot = face->glyph;
                error = FT_Get_Glyph( slot, &glyphArray[ curIndex ] );
                if ( error )
                    continue;

                // Get the bounding box:
                FT_BBox bbox;
                FT_Glyph_Get_CBox( glyphArray[ curIndex ], FT_GLYPH_BBOX_PIXELS, &bbox );
                if ( bbox.yMax > maxTop )
                    maxTop = bbox.yMax;

                // Render the width marker
                FT_Glyph_Metrics metrics = face->glyph->metrics;
                mGlyphWidths[ curIndex ] = ( slot->advance.x >> 6 );
                int right = left + mGlyphWidths[ curIndex ];
                widthDC.DrawLine( right, top, right, top + fontDef.mCellHeight );

                // Increment the current glyph:
                curGlyph++;
            }
        }

        // Render the glyphs:
        for ( int y = 0; y < vCells; y++ )
        {
            int top = y * fontDef.mCellHeight;
            int bottom = top + fontDef.mCellHeight;
            int baseLine = top + maxTop;
            for ( int x = 0; x < hCells; x++ )
            {
                int left = x * fontDef.mCellWidth;
                FT_Glyph image = glyphArray[ y * hCells + x ];
                FT_BBox bbox;
                FT_Glyph_Get_CBox( image, FT_GLYPH_BBOX_PIXELS, &bbox );
                FT_Vector pos;
                pos.x = 0;
                pos.y = 0;
                error = FT_Glyph_To_Bitmap( &image, ( antiAliased ) ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO, &pos, 0 );
                if ( error )
                    continue;

                FT_BitmapGlyph bmp = (FT_BitmapGlyph)image;

                if ( bmp->bitmap.pixel_mode > 1 )
                    RenderGlyph( &bmp->bitmap, left + maxLeft, top + maxTop - bbox.yMax );
                else
                {
                    FT_Bitmap tempbitmap;
                    FT_Bitmap_New(&tempbitmap);
                    FT_Bitmap_Convert( fontLib, &bmp->bitmap, &tempbitmap, 1 );
 
                    RenderGlyphAliased( &tempbitmap, left + maxLeft, top + maxTop - bbox.yMax );

                    FT_Bitmap_Done( fontLib, &tempbitmap );
                }
                widthDC.DrawLine( left, top + maxTop - bbox.yMax, left + maxLeft, top + maxTop - bbox.yMax );

                // Render the underline, if necessary
                if ( fontDef.mIsUnderline )
                {
                    int underlinePos = 0, underlineThickness = 0;
                    underlinePos = baseLine - ( face->underline_position >> 6 );
                    underlineThickness = ( face->underline_thickness >> 6 );

                    for ( int y = underlinePos; y < underlinePos + underlineThickness; y++ )
                    {
                        int width = bbox.xMax - bbox.xMin;
                        for ( int x = left + maxLeft; x < left + maxLeft + width; x++ )
                        {
                            mPreviewImage.SetRGB( x, y, 255, 255, 255 );
                            mPreviewImage.SetAlpha( x, y, 255 );
                        }
                    }
                }

                FT_Done_Glyph( image );
            }
        }

        FT_Done_Face( face );
    }

    // Create the grid lines:
    for ( int y = 1; y <= vCells; y++ )
    {
        int vPos = y * fontDef.mCellHeight;
        gridDC.DrawLine( 0, vPos, fontDef.mImageWidth, vPos );
    }
    for ( int x = 1; x <= hCells; x++ )
    {
        int hPos = x * fontDef.mCellWidth;
        gridDC.DrawLine( hPos, 0, hPos, fontDef.mImageHeight );
    }

    //fontDC.SelectObject( wxNullBitmap );
    gridDC.SelectObject( wxNullBitmap );
    widthDC.SelectObject( wxNullBitmap );

    // Set masks for the overlays:
    mGridOverlay.SetMask( new wxMask( mGridOverlay, *wxBLACK ) );
    mWidthsOverlay.SetMask( new wxMask( mWidthsOverlay, *wxBLACK ) );

    Refresh();
    Update();
}

//RenderGlyph
void PreviewWindow::RenderGlyph( FT_Bitmap* bitmap, size_t x, size_t y )
{
    if ( !bitmap->buffer )
        return;
    assert( bitmap->rows > 0 && bitmap->width > 0 );

    // Iterate over the glyph bitmap
    for ( int posY = 0; posY < bitmap->rows; posY++ )
    {
        for ( int posX = 0; posX < bitmap->width; posX++ )
        {
            // If there is something in the bitmap, copy it to the image
            if ( bitmap->buffer[ posY * bitmap->width + posX ] )
            {
                int plotX = posX + x, plotY = posY + y;
                if ( plotX >= 0 && plotX < mPreviewImage.GetWidth() && plotY >= 0 && plotY < mPreviewImage.GetHeight() )
                {
                    mPreviewImage.SetRGB( posX + x, posY + y, 255, 255, 255 );
                    mPreviewImage.SetAlpha( posX + x, posY + y, bitmap->buffer[ posY * bitmap->width + posX ] );
                }
            }
        }
    }
}

void PreviewWindow::RenderGlyphAliased( FT_Bitmap* bitmap, size_t x, size_t y )
{
    if ( !bitmap->buffer )
        return;
    assert( bitmap->rows > 0 && bitmap->width > 0 );

    // Iterate over the glyph bitmap
    for ( int posY = 0; posY < bitmap->rows; posY++ )
    {
        for ( int posX = 0; posX < bitmap->width; posX++ )
        {
            // If there is something in the bitmap, copy it to the image
            if ( bitmap->buffer[ posY * bitmap->width + posX ] )
            {
                int plotX = posX + x, plotY = posY + y;
                if ( plotX >= 0 && plotX < mPreviewImage.GetWidth() && plotY >= 0 && plotY < mPreviewImage.GetHeight() )
                {
                    mPreviewImage.SetRGB( posX + x, posY + y, 255, 255, 255 );
                    mPreviewImage.SetAlpha( posX + x, posY + y, 255 );
                }
            }
        }
    }
}

//OnPaint
void PreviewWindow::OnPaint( wxPaintEvent& event )
{
    wxPaintDC dc( this );
    PrepareDC(dc);
    dc.DrawBitmap( mCanvas, 0, 0, false );
    dc.DrawBitmap( wxBitmap( mPreviewImage ), 0, 0, false );

    // Get a pointer to the main frame (to check whether to display the grid and width lines)
    FontsetPanelExt* parent = wxDynamicCast( GetParent(), FontsetPanelExt );
    if ( parent )
    {
        if ( parent->GetViewGridlines() )
            dc.DrawBitmap( mGridOverlay, 0, 0, true );
        if ( parent->GetViewWidths() )
            dc.DrawBitmap( mWidthsOverlay, 0, 0, true );
    }
    //dc.SetBackground( *wxBLUE_BRUSH );
    //dc.Clear();
}

//SavePNG
void PreviewWindow::SavePNG( const StringUtil::String& fileName ) const
{
    if ( !mPreviewImage.SaveFile( wxString( fileName.c_str() ), wxBITMAP_TYPE_PNG ))
        wxLogError(wxT("Can't save file"));
}

//SetDisplayGrid
void PreviewWindow::SetDisplayGrid( bool flag )
{
    mDisplayGrid = flag;
}

//GetDisplayGrid
bool PreviewWindow::GetDisplayGrid() const
{
    return mDisplayGrid;
}

//SetDisplayWidths
void PreviewWindow::SetDisplayWidths( bool flag )
{
    mDisplayWidths = flag;
}

//GetDisplayWidths
bool PreviewWindow::GetDisplayWidths() const
{
    return mDisplayWidths;
}

//GetGlyphWidths
void PreviewWindow::GetGlyphWidths( PreviewWindow::WidthsArrayType* widths ) const
{
    widths->clear();
    widths->assign( mGlyphWidths.begin(), mGlyphWidths.end() );
}
